package com.mall.product.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.mall.product.constants.RedisENUM;
import com.mall.product.entity.AttrEntity;
import com.mall.product.service.CategoryBrandRelationService;
import com.mall.product.vo.Catalog2VO;
import com.sun.org.apache.xml.internal.resolver.CatalogEntry;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.orm.jpa.hibernate.SpringImplicitNamingStrategy;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.annotation.Caching;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.common.utils.PageUtils;
import com.common.utils.Query;

import com.mall.product.dao.CategoryDao;
import com.mall.product.entity.CategoryEntity;
import com.mall.product.service.CategoryService;
import org.springframework.transaction.annotation.Transactional;


@Service("categoryService")
@Slf4j
public class CategoryServiceImpl extends ServiceImpl<CategoryDao, CategoryEntity> implements CategoryService {
    @Autowired
    private CategoryBrandRelationService categoryBrandRelationService;
    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    @Autowired
    private RedissonClient redissonClient;
    @Override
    public PageUtils queryPage(Map<String, Object> params) {
        IPage<CategoryEntity> page = this.page(
                new Query<CategoryEntity>().getPage(params),
                new QueryWrapper<CategoryEntity>()
        );

        return new PageUtils(page);
    }

    /**
     * 查询所有的类别数据，然后将数据封装为树形结构，便于前端使用
     *
     * @param
     * @return
     */
    @Override
    public List<CategoryEntity> queryPageWitTree(Map<String, Object> params) {
        List<CategoryEntity> categoryEntities = baseMapper.selectList(null);
        List<CategoryEntity> list = new ArrayList<>();
        for (CategoryEntity categoryEntity : categoryEntities) {
            if (categoryEntity.getParentCid() == 0){
                list.add(categoryEntity);
            }
        }
        list.sort(Comparator.comparing(CategoryEntity::getSort));
        getCategoryChildres(list,categoryEntities);
        return list;
    }

    private void getCategoryChildres(List<CategoryEntity> list, List<CategoryEntity> categoryEntities) {
        for (CategoryEntity categoryEntity : list) {
            List<CategoryEntity> categoryEntityList = new ArrayList<>();
            for (CategoryEntity entity : categoryEntities) {
                if (categoryEntity.getCatId().equals(entity.getParentCid())){
                    categoryEntityList.add(entity);
                    categoryEntity.setChildrens(categoryEntityList);
                }
//                categoryEntityList.sort(Comparator.comparing(CategoryEntity::getSort));
            }
            getCategoryChildres(categoryEntityList,categoryEntities);
        }
    }

    /**
     * 根据id修改状态码
     *
     * @param list
     */
    @Override
    public void removeCategoryByIds(List<Long> list) {
        //TODO 判断商品是否被引用
        baseMapper.deleteBatchIds(list);
    }
    @Override
    public Long[] findCateLogPath(Long cateLogId) {
        List<Long> paths = new ArrayList<>();
        List<Long> parentPath = findParentPath(cateLogId, paths);
        Collections.reverse(parentPath);
        return parentPath.toArray(new Long[0]);
    }

    /**
     * 更新类别名称
     * @param  category
     */
    @Override
    @Transactional
//    @CacheEvict(value = "catagory",key = "'getLevelCategory'")
//    @Caching(evict = {@CacheEvict(value = "catagory",key = "'getLevelCategory'"),
//            @CacheEvict(value = "catagory",key = "'getCatalog2JSON'")})
    @CacheEvict(value = "catagory",allEntries = true)
    public void updateDetail(CategoryEntity category) {
        this.updateById(category);
        if (StringUtils.isNotEmpty(category.getName())){
            categoryBrandRelationService.updateCateLogName(category.getName(),category.getCatId());
        }
    }


    private List<Long> findParentPath(Long cateLogPath,List<Long> paths){
        paths.add(cateLogPath);
        CategoryEntity category = this.getById(cateLogPath);
        if (category.getParentCid() != 0){
            findParentPath(category.getParentCid(),paths);
        }
        return paths;
    }

    /**
     * 查询所有商品的一级分类
     * @Cacheable({"catagory","product"})代表当前返回的结果需要缓存的
     * @return
     */
    @Override
    @Cacheable(value = {"catagory","product"},key = "#root.method.name")
    public List<CategoryEntity> getLevelCategory() {
        List<CategoryEntity> list = baseMapper.queryLeve1Category();
        return list;
    }
    @Override
    @Cacheable(value = "catagory",key = "#root.method.name")
    public Map<String, List<Catalog2VO>> getCatalog2JSON(){
        String catalogJSON = stringRedisTemplate.opsForValue().get(RedisENUM.REDIS_CATEGORY);
        if (StringUtils.isNotEmpty(catalogJSON)) {
            Map<String, List<Catalog2VO>> stringListMap = JSON.parseObject(catalogJSON, new TypeReference<Map<String, List<Catalog2VO>>>() {
            });
            System.out.println("操作缓存");
            return stringListMap;
        }
        System.out.println("缓存未命中");
            Map<String, List<Catalog2VO>> catalog2JSONForDb = getCatalog2JSONForDbWithRedis();
//            if (ObjectUtil.isNull(catalog2JSONForDb)){
//                stringRedisTemplate.opsForValue().set(RedisENUM.REDIS_CATEGORY,"1",5, TimeUnit.SECONDS);
//            }
//            String json = JSON.toJSONString(catalog2JSONForDb);
//            stringRedisTemplate.opsForValue().set(RedisENUM.REDIS_CATEGORY, json,10,TimeUnit.SECONDS);
            return catalog2JSONForDb;
    }



    public Map<String, List<Catalog2VO>> getCatalog2JSONForDb() {
        synchronized (this) {
            return getListMap();
        }
    }
    private List<CategoryEntity> queryByParentCid(List<CategoryEntity> list,Long parentCid){
        List<CategoryEntity> categoryEntities = list.stream().filter(item -> {
            return item.getParentCid().equals(parentCid);
        }).collect(Collectors.toList());
        return categoryEntities;
    }

    /**
     * redisson实现
     * @return
     */
    public Map<String, List<Catalog2VO>> getCatalog2JSONForDbWithRedisson() {
        final String keys = "redisson_lock_catalogJSON";
//        String  uuid = UUID.randomUUID().toString();
//        Boolean lock = stringRedisTemplate.opsForValue().setIfAbsent(keys, uuid,30,TimeUnit.SECONDS);
//        if (Boolean.TRUE.equals(lock)){
        RLock lock = redissonClient.getLock(keys);
        Map<String, List<Catalog2VO>> map = null;
            try {
                lock.lock();
                map = getListMap();
            } finally {
//            String val = stringRedisTemplate.opsForValue().get(keys);
//            if (uuid.equals(val)) {
//                stringRedisTemplate.delete(keys);
//            }
//                String srcipts = "if redis.call('get',KEYS[1]) == ARGV[1]  then return redis.call('del',KEYS[1]) else  return 0 end ";
//
//                stringRedisTemplate.execute(new DefaultRedisScript<Long>(srcipts, Long.class)
//                        , Arrays.asList(keys), uuid);
                lock.unlock();
            }
            return map;
    }
    /**
     * redis实现分布式锁
     * @return
     */
    public Map<String, List<Catalog2VO>> getCatalog2JSONForDbWithRedis() {
        final String keys = "lock";
        String  uuid = UUID.randomUUID().toString();
        Boolean lock = stringRedisTemplate.opsForValue().setIfAbsent(keys, uuid,30,TimeUnit.SECONDS);
        if (Boolean.TRUE.equals(lock)){
            Map<String, List<Catalog2VO>> map;
            try {
                map = getListMap();
            } finally {
//            String val = stringRedisTemplate.opsForValue().get(keys);
//            if (uuid.equals(val)) {
//                stringRedisTemplate.delete(keys);
//            }
                String srcipts = "if redis.call('get',KEYS[1]) == ARGV[1]  then return redis.call('del',KEYS[1]) else  return 0 end ";

                stringRedisTemplate.execute(new DefaultRedisScript<Long>(srcipts, Long.class)
                        , Arrays.asList(keys), uuid);
            }
            return map;
        }else {
            return getCatalog2JSONForDbWithRedis();
        }
    }

    private Map<String, List<Catalog2VO>> getListMap() {
        String string = stringRedisTemplate.opsForValue().get(RedisENUM.REDIS_CATEGORY);
        if (StringUtils.isNotEmpty(string)){
            Map<String, List<Catalog2VO>> stringListMap = JSON.parseObject(string, new TypeReference<Map<String, List<Catalog2VO>>>() {
            });
            return stringListMap;
        }
        List<CategoryEntity> list = baseMapper.selectList(new QueryWrapper<CategoryEntity>());
        List<CategoryEntity> leve1Category = this.queryByParentCid(list,0l);
        Map<String, List<Catalog2VO>> map = leve1Category.stream().collect(Collectors.toMap(
                key -> key.getCatId().toString()
                , value -> {
                    List<CategoryEntity> l2Catalogs = this.queryByParentCid(list,value.getCatId());
                    List<Catalog2VO> Catalog2VOs =null;
                    if(l2Catalogs != null){
                        Catalog2VOs = l2Catalogs.stream().map(l2 -> {
                            Catalog2VO catalog2VO = new Catalog2VO(l2.getParentCid().toString(), null, l2.getCatId().toString(), l2.getName());
                            List<CategoryEntity> l3Catelogs = this.queryByParentCid(list,l2.getCatId());
                            if(l3Catelogs != null){
                                List<Catalog2VO.Catalog3VO> catalog3VOS = l3Catelogs.stream().map(l3 -> {
                                    Catalog2VO.Catalog3VO catalog3VO = new Catalog2VO.Catalog3VO(l3.getParentCid().toString(), l3.getCatId().toString(), l3.getName());
                                    return catalog3VO;
                                }).collect(Collectors.toList());
                                catalog2VO.setCatalog3List(catalog3VOS);
                            }
                            return catalog2VO;
                        }).collect(Collectors.toList());
                    }

                    return Catalog2VOs;
                }
        ));
            if (ObjectUtil.isNull(map)){
                stringRedisTemplate.opsForValue().set(RedisENUM.REDIS_CATEGORY,"1",100,TimeUnit.MINUTES);
            }else {
                String json = JSON.toJSONString(map);
                stringRedisTemplate.opsForValue().set(RedisENUM.REDIS_CATEGORY,json);
            }

        return map;
    }
}